package com.view.damp

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.animation.DecelerateInterpolator
import android.widget.ListView
import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.NestedScrollingParent
import androidx.core.view.NestedScrollingParentHelper
import androidx.core.view.ViewCompat
import androidx.core.widget.ListViewCompat
import com.view.refresh.IFreshLayout
import kotlin.math.abs

/**
 * @describe:竖直刷新容器，顶部和底部都具有弹性效果，仿照官方的SwipeRefreshLayout
 * @Author: lixiaopeng
 * @Date: 2020/6/10
 */
class VerticalDampWrapper : ViewGroup, NestedScrollingParent, NestedScrollingChild {

    private val TAG = "VerticalDampWrapper"

    private val INVALID_POINTER = -1

    //被包裹的控件
    private var targetView: View? = null

    //越界后展示的View，不传空白
    private var startLayout: IFreshLayout? = null//头部控件，非必须
    private var endLayout: IFreshLayout? = null//底部控件，非必须

    private val touchSlop: Int

    //阻尼系数
    private val dampRate = .5f

    //回弹加速器
    private val interpolator: DecelerateInterpolator

    //嵌套滚动
    private val nestedScrollingParentHelper: NestedScrollingParentHelper
    private val nestedScrollingChildHelper: NestedScrollingChildHelper
    private val mParentScrollConsumed = IntArray(2)
    private val mParentOffsetInWindow = IntArray(2)

    private var activePointerId = INVALID_POINTER

    //偏移量
    private var offset = 0

    //是否正在刷新
    private var refreshing = false

    //是否正在返回头部或者尾部
    private var returningStart = false

    //是否正在被拖拽
    private var isBeingDragged = false

    //按下的y
    private var initialDownY = 0f
    private var mLastMotionY = 0f

    //未消耗的距离
    private var mTotalUnconsumed = 0f

    private var nestedScrolling = false


    constructor(context: Context?) : this(context, null)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        touchSlop = ViewConfiguration.get(context).scaledTouchSlop
        interpolator = DecelerateInterpolator(2f)
        nestedScrollingParentHelper = NestedScrollingParentHelper(this)
        nestedScrollingChildHelper = NestedScrollingChildHelper(this)
        isNestedScrollingEnabled = true
    }

    private fun ensureTarget(): Boolean {
        if (childCount == 0) {
            return false
        }
        if (targetView != null) {
            return true
        }
        for (index in 0 until childCount) {
            val childAt = getChildAt(index)
            if (childAt != startLayout && childAt != endLayout) {
                targetView = childAt
                break
            }
        }
        return targetView != null
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        if (childCount == 0) {
            return
        }
        if (!ensureTarget()) {
            return
        }
        val parentWidth = measuredWidth
        val parentHeight = measuredHeight
        targetView?.let {
            val childLeft = paddingLeft
            val childTop = paddingTop
            val childWidth = parentWidth - paddingLeft - paddingRight
            val childHeight = parentHeight - paddingTop - paddingBottom
            //内容区布局
            it.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight)
        }
        //头布局
        startLayout?.apply {
            val headHeight = this.viewHeight
            val headWidth = targetView?.measuredWidth ?: 0
            layout(parentWidth / 2 - headWidth / 2, -headHeight, parentWidth / 2 + headWidth / 2, 0)
        }
        //脚布局
        endLayout?.apply {
            val footHeight = this.viewHeight
            val footWidth = targetView?.measuredWidth ?: 0
            layout(parentWidth / 2 - footWidth / 2, parentHeight, parentWidth / 2 + footWidth / 2, parentHeight + footHeight)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (!ensureTarget()) {
            return
        }
        //content布局
        targetView?.measure(MeasureSpec.makeMeasureSpec(
                measuredWidth - paddingLeft - paddingRight,
                MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                measuredHeight - paddingTop - paddingBottom, MeasureSpec.EXACTLY))
        //头布局
        startLayout?.apply {
            measure(MeasureSpec.makeMeasureSpec(
                    this.viewHeight,
                    MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                    targetView?.measuredWidth ?: 0, MeasureSpec.EXACTLY))
        }
        //脚布局
        endLayout?.apply {
            measure(MeasureSpec.makeMeasureSpec(
                    this.viewHeight,
                    MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                    targetView?.measuredWidth ?: 0, MeasureSpec.EXACTLY))
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (!ensureTarget()) {
            return false
        }

        val action = ev.action
        var pointIndex = INVALID_POINTER
        if (returningStart && action == MotionEvent.ACTION_DOWN) {
            returningStart = false
        }

        if (!isEnabled || returningStart || refreshing || nestedScrolling) {
            return false
        }

        when (action) {
            MotionEvent.ACTION_DOWN -> {
                activePointerId = ev.getPointerId(0)
                isBeingDragged = false
                pointIndex = ev.findPointerIndex(activePointerId)
                if (pointIndex < 0) {
                    return false
                }
                initialDownY = ev.getY(pointIndex)
                mLastMotionY = ev.getY(pointIndex)
            }
            MotionEvent.ACTION_MOVE -> {
                if (activePointerId == INVALID_POINTER) {
                    return false
                }
                pointIndex = ev.findPointerIndex(activePointerId)
                if (pointIndex < 0) {
                    return false
                }
                val y = ev.getY(pointIndex)
                val dy = y - initialDownY
                if (dy > 0 && !canChildScrollUp()) {
                    startDragging(y)
                    return true
                } else if (dy < 0 && !canChildScrollDown()) {
                    startDragging(y)
                    return true
                }
            }

            MotionEvent.ACTION_POINTER_UP -> {
                onSecondaryPointerUp(ev)
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                isBeingDragged = false
                activePointerId = INVALID_POINTER
            }
        }
        return isBeingDragged
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val action = event.actionMasked
        var pointerIndex = INVALID_POINTER
        if (returningStart && action == MotionEvent.ACTION_DOWN) {
            returningStart = false
        }

        if (!isEnabled || returningStart || refreshing || nestedScrolling) {
            return false
        }
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                activePointerId = event.getPointerId(0)
                isBeingDragged = false
            }
            MotionEvent.ACTION_MOVE -> {
                pointerIndex = event.findPointerIndex(activePointerId)
                if (pointerIndex < 0) {
                    return false
                }
                val y = event.getY(pointerIndex)
                val dy = y - initialDownY
                if (dy > 0 && !canChildScrollUp()) {
                    startDragging(y)
                } else if (dy < 0 && !canChildScrollDown()) {
                    startDragging(y)
                }
                if (isBeingDragged) {
                    val overScrollTop: Float = (y - initialDownY) * dampRate
                    if (abs(overScrollTop) > 0f) {
                        moveTarget(overScrollTop)
                    } else {
                        return false
                    }
                }
            }

            MotionEvent.ACTION_POINTER_DOWN -> {
                pointerIndex = event.actionIndex
                if (pointerIndex < 0) {
                    return false
                }
                activePointerId = event.getPointerId(pointerIndex)
            }

            MotionEvent.ACTION_POINTER_UP -> {
                onSecondaryPointerUp(event)
            }
            MotionEvent.ACTION_UP -> {
                pointerIndex = event.findPointerIndex(activePointerId)
                if (pointerIndex < 0) {
                    return false
                }
                if (isBeingDragged) {
                    val y = event.getY(pointerIndex)
                    isBeingDragged = false
                    finishMove()
                }
                mLastMotionY = 0f
                activePointerId = INVALID_POINTER
            }

        }
        return true
    }

    // NestedScrollingParent start
    override fun onStartNestedScroll(child: View, target: View, axes: Int): Boolean {
        return (isEnabled && !returningStart && !refreshing
                && axes and ViewCompat.SCROLL_AXIS_VERTICAL != 0)
    }

    override fun onNestedScrollAccepted(child: View, target: View, axes: Int) {
        nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes)
        startNestedScroll(axes and ViewCompat.SCROLL_AXIS_VERTICAL)
        mTotalUnconsumed = 0f
        nestedScrolling = true
    }

    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {
        Log.d(TAG, "onNestedPreScroll: " + dy + "     " + mTotalUnconsumed.toInt())
        if (!canChildScrollUp()) {
            if (dy > 0 && mTotalUnconsumed > 0) {//向上，总的未消费>0
                if (dy > mTotalUnconsumed) {
                    Log.e(TAG, "onNestedPreScroll: 1111111")
                    consumed[1] = dy - mTotalUnconsumed.toInt()
                    mTotalUnconsumed = 0f
                } else {
                    Log.e(TAG, "onNestedPreScroll: 222222222")
                    mTotalUnconsumed -= dy.toFloat()
                    consumed[1] = dy
                }
                moveTarget(mTotalUnconsumed * dampRate)
            }
        } else if (!canChildScrollDown()) {

            if (dy < 0 && mTotalUnconsumed < 0) {//向上，总的未消费>0
                if (dy < mTotalUnconsumed) {
                    Log.e(TAG, "onNestedPreScroll: 111111")
                    consumed[1] = dy - mTotalUnconsumed.toInt()
                    mTotalUnconsumed = 0f
                } else {
                    Log.e(TAG, "onNestedPreScroll: 222222")
                    mTotalUnconsumed -= dy.toFloat()
                    consumed[1] = dy
                }
                moveTarget(mTotalUnconsumed * dampRate)
            }
        }
        //当作为嵌套子类时会用到
        val parentConsumed = mParentScrollConsumed
        if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
            consumed[0] += parentConsumed[0]
            consumed[1] += parentConsumed[1]
        }
    }

    override fun getNestedScrollAxes(): Int {
        return nestedScrollingParentHelper.nestedScrollAxes
    }

    override fun onStopNestedScroll(child: View) {
        nestedScrollingParentHelper.onStopNestedScroll(child)
        nestedScrolling = false
        if (mTotalUnconsumed != 0f) {
            finishMove()
            mTotalUnconsumed = 0f
        }
        stopNestedScroll()
    }

    override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) {
        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
                mParentOffsetInWindow)
        val dy = dyUnconsumed + mParentOffsetInWindow[1]
        if (dy < 0 && !canChildScrollUp()) {
            mTotalUnconsumed += Math.abs(dy).toFloat()
            moveTarget(mTotalUnconsumed * dampRate)
        } else if (dy > 0 && !canChildScrollDown()) {
            mTotalUnconsumed -= Math.abs(dy).toFloat()
            moveTarget(mTotalUnconsumed * dampRate)
        }
    }
    // NestedScrollingParent end

    //NestedScrollingChild start
    override fun setNestedScrollingEnabled(enabled: Boolean) {
        nestedScrollingChildHelper.isNestedScrollingEnabled = enabled
    }

    override fun isNestedScrollingEnabled(): Boolean {
        return nestedScrollingChildHelper.isNestedScrollingEnabled
    }

    override fun startNestedScroll(axes: Int): Boolean {
        return nestedScrollingChildHelper.startNestedScroll(axes)
    }

    override fun stopNestedScroll() {
        nestedScrollingChildHelper.stopNestedScroll()
    }

    override fun hasNestedScrollingParent(): Boolean {
        return nestedScrollingChildHelper.hasNestedScrollingParent()
    }

    override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?): Boolean {
        return nestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
    }

    override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean {
        return nestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
    }

    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
        return dispatchNestedPreFling(velocityX, velocityY)
    }

    override fun onNestedFling(target: View, velocityX: Float, velocityY: Float,
                               consumed: Boolean): Boolean {
        return dispatchNestedFling(velocityX, velocityY, consumed)
    }

    override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
        return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)
    }

    override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
        return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
    }
    //NestedScrollingChild end


    private fun finishMove() {
        //结束回滚到某状态
        val top: Float = targetView?.translationY ?: 0f

        targetView?.translationY = 0f
        startLayout?.freshView?.translationY = 0f
        endLayout?.freshView?.translationY = 0f
    }

    /**
     * 移动contentView
     */
    private fun moveTarget(offset: Float) {
        startLayout?.freshView?.apply { translationY = offset }
        targetView?.apply { translationY = offset  }
        endLayout?.freshView?.apply { translationY = offset  }
    }

    private fun onSecondaryPointerUp(ev: MotionEvent) {
        val pointerIndex = ev.actionIndex
        val pointerId = ev.getPointerId(pointerIndex)
        if (pointerId == activePointerId) {
            val newPointerIndex = if (pointerId == 0) 1 else 0
            activePointerId = ev.getPointerId(newPointerIndex)
        }
    }

    /**
     * 开始拖拽
     */
    private fun startDragging(y: Float) {
        val yDiff = y - initialDownY
        if (abs(yDiff) > touchSlop && !isBeingDragged) {
            initialDownY += touchSlop
            isBeingDragged = true
        }
    }

    fun canChildScrollUp(): Boolean {
        targetView?.apply {
            if (this is ListView) {
                return ListViewCompat.canScrollList(this, -1)
            }
            return canScrollVertically(-1)
        }
        return false
    }

    fun canChildScrollDown(): Boolean {
        targetView?.apply {
            if (this is ListView) {
                return ListViewCompat.canScrollList(this, 1)
            }
            return canScrollVertically(1)
        }
        return false
    }
}